/*
原文链接：https://www.llfc.club/articlepage?id=2Qld2hoFIu8ycYBJXQdxwyWEBfy

IOServicePool多线程模式特点

1   每一个io_context跑在不同的线程里，所以同一个socket会被注册在同一个io_context里，它的回调函数也会被单独的一个线程回调，
那么对于同一个socket，他的回调函数每次触发都是在同一个线程里，就不会有线程安全问题，网络io层面上的并发是线程安全的。

2   但是对于不同的socket，回调函数的触发可能是同一个线程(两个socket被分配到同一个io_context)，也可能不是同一个线程
(两个socket被分配到不同的io_context里)。所以如果两个socket对应的上层逻辑处理，如果有交互或者访问共享区，会存在线程安全问题。
比如socket1代表玩家1，socket2代表玩家2，玩家1和玩家2在逻辑层存在交互，比如两个玩家都在做工会任务，他们属于同一个工会，
工会积分的增加就是共享区的数据，需要保证线程安全。可以通过加锁或者逻辑队列的方式解决安全问题，我们目前采取了后者。

3   多线程相比单线程，极大的提高了并发能力，因为单线程仅有一个io_context服务用来监听读写事件，就绪后回调函数在一个线程里串行调用,
 如果一个回调函数的调用时间较长肯定会影响后续的函数调用，毕竟是穿行调用。而采用多线程方式，可以在一定程度上减少前一个逻辑调用影响
 下一个调用的情况，比如两个socket被部署到不同的iocontext上，但是当两个socket部署到同一个iocontext上时仍然存在调用时间影响的问题。
 不过我们已经通过逻辑队列的方式将网络线程和逻辑线程解耦合了，不会出现前一个调用时间影响下一个回调触发的问题。
*/
#ifndef IO_SERVER_H 
#define IO_SERVER_H 

#include <cstdint>
#include <utility>
#include <vector>
#include <memory>
#include <thread>

#include <boost/asio.hpp>

#include "singleton.h"


class IOContextPool : public Singleton<IOContextPool>
{
    friend Singleton<IOContextPool>; //目的是为了让Singleton类访问到本类的构造函数
public:
    using IOContext = boost::asio::io_context;  //只有在注册了事件之后io_context::run才会阻塞
    using Work = boost::asio::io_context::work; //而绑定了work之后，不需要注册事件，io_context::run也会阻塞
    using WorkPtr = std::unique_ptr<Work>;

    IOContextPool(const IOContextPool&) = delete;
    IOContextPool& operator=(const IOContextPool&) = delete;
    ~IOContextPool();

    IOContext& GetIOService();
    void Stop();

private:
    IOContextPool(std::size_t thread_num = std::thread::hardware_concurrency());

private:
    uint16_t                 m_ioc_index;   //当前io_context的索引
    std::vector<IOContext>   m_io_contexts;
    std::vector<WorkPtr>     m_io_work;
    std::vector<std::thread> m_threads;
};

#endif	/* IO_SERVER_H */
